package cn.tedu.antcolony.passport.filter;

import cn.tedu.antcolony.passport.mapper.AdminMapper;
import cn.tedu.antcolonycommons.consts.AdminInfoCacheConsts;
import cn.tedu.antcolonycommons.consts.HttpConsts;
import cn.tedu.antcolonycommons.consts.JwtCacheConsts;
import cn.tedu.antcolonycommons.pojo.po.AdminLoginInfoPO;
import cn.tedu.antcolonycommons.security.LoginPrincipal;
import cn.tedu.antcolonycommons.util.JwtUtils;
import cn.tedu.antcolonycommons.util.RedisTemplateUtils;
import cn.tedu.antcolonycommons.web.JsonResult;
import cn.tedu.antcolonycommons.web.ServiceCode;
import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.Claims;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * jwt过滤器类
 */
@Slf4j
@Component
public class JwtAuthorizationFilter extends OncePerRequestFilter {
    @Autowired
    private JwtUtils jwtUtils;

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private RedisTemplateUtils redisTemplateUtils;

    @Value("${antcolony.jwt.secret-key}")
    String secretKey;
    //过期时间 分钟
    @Value("${antcolony.jwt.duration-in-minute}")
    private Long durationInMinute;

    @Autowired
    private AdminMapper adminMapper;



    @SneakyThrows
    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        log.debug("JwtAuthorizationFilter，过滤器类开始执行!");
        //放行登录
        String requestURI = httpServletRequest.getRequestURI();
        if (requestURI.equals("/admin/login")){
            filterChain.doFilter(httpServletRequest,httpServletResponse);
            return;
        }
        // 根据业内惯用的做法，客户端会将JWT放在请求头（Request Header）中的Authorization属性中
        String jwt = httpServletRequest.getHeader(HttpConsts.HEADER_AUTHORIZATION);
        log.debug("客户端携带的jwt:{}",jwt);
        //判断客户端是否携带了有效的jwt 不为null 不为""
        if (!StringUtils.hasText(jwt)){
            //如果jwt无效则放行
            filterChain.doFilter(httpServletRequest,httpServletResponse);
            log.warn("Authorization,为空!");
            return;
        }
        String jwtToken = jwt.replace(HttpConsts.AUTHORIZATION_BEARER, "");
        if(!StringUtils.hasText(jwtToken)){
            filterChain.doFilter(httpServletRequest,httpServletResponse);
            log.warn("jwtToken为空!");
            return;
        }

        //效验JwtToken
        boolean result = jwtUtils.verifyToken(httpServletRequest,httpServletResponse,jwtToken);
        if (!result){
            log.warn("jwtToken非法!");
            return;
        }

        //用户登录ip
        String remoteAddr = httpServletRequest.getRemoteAddr();
        //用户登录浏览器
        String userAgent = httpServletRequest.getHeader(HttpConsts.HEADER_USER_AGENT);
        //从jwt中获取用户信息
        Claims user = jwtUtils.getUser(jwtToken);
        Long id= user.get(AdminInfoCacheConsts.ADMIN_ID,Long.class);
        String username =  user.get(AdminInfoCacheConsts.ADMIN_USERNAME,String.class);
        //从 Redis中获取 用户登录信息 进行验证
        AdminLoginInfoPO adminLoginInfoPO = (AdminLoginInfoPO) redisTemplateUtils.get(JwtCacheConsts.ADMIN_JWT_PREFIX + jwtToken);
        //判断JWT是否过期
        if (adminLoginInfoPO==null){
            String message="用携带JWT过期，需要从新登陆!";
            filterChain.doFilter(httpServletRequest,httpServletResponse);
            return;
        }
        String remoteAddrRds= adminLoginInfoPO.getRemoteAddr();
        String userAgentRds = adminLoginInfoPO.getUserAgent();
        //判断 JWT是否被盗用
        if (!remoteAddr.equals(remoteAddrRds)&&!userAgent.equals(userAgentRds)){
            String message="Jwt可能被盗用!";
            log.debug(message);
            filterChain.doFilter(httpServletRequest,httpServletResponse);
            return;
        }
        //用户id
        Long idRedis = adminLoginInfoPO.getId();
        //判断用户登录状态
        Integer enable = adminLoginInfoPO.getEnable();
        if (!enable.equals(AdminInfoCacheConsts.ADMIN_ENABLE)){//该账号已被警用
            String message="登录失败,该账号已被禁用!";
            JsonResult<Void> failResult = JsonResult.fail(ServiceCode.ERR_UNAUTHORIZED_DISABLED, message);
            jwtUtils.printToken(httpServletRequest,httpServletResponse,failResult);
            return;
        }

        //创建当事人对象
        LoginPrincipal loginPrincipal=LoginPrincipal.builder()
                .id(id)
                .username(username).build();
        //获取 用户权限
        String authorityListJsonString = adminLoginInfoPO.getAuthorityListJsonString();
        List<SimpleGrantedAuthority> simpleGrantedAuthorities = JSON.parseArray(authorityListJsonString, SimpleGrantedAuthority.class);

        //创建Authentication对象
        Authentication usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(loginPrincipal, null, simpleGrantedAuthorities);

        //将Authentication对象 存入SecurityContext应用上下文中
        SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);

        /**
         * 如果 当前用户在 Jwt有效期2个小时内 仍在活跃  并且有效期 <30分钟，就进行 Jwt续签
         * 判断 Jwt 是否需要延长时间
         */
        //从 redis中获取 当前JWT 的存活时间
        long getexpire = redisTemplateUtils.getexpire(JwtCacheConsts.ADMIN_JWT_PREFIX + jwtToken);
        //判断 Jwt的有效是否<30分钟
        if (getexpire<=AdminInfoCacheConsts.JWT_TIME_THRESHOLD){
            //从新响应 Jwt  进行Jwt续签
            Map<String,Object> claims=new HashMap<>();
            claims.put(AdminInfoCacheConsts.ADMIN_ID,id);
            claims.put(AdminInfoCacheConsts.ADMIN_USERNAME,username);
            //redis key过期时间 120分钟
            Long tokenDuration=durationInMinute*60L*1000;
            //Jwt 过期时间
            Date tokenexpireDate=new Date(System.currentTimeMillis()+tokenDuration);
            //刷新Jwt进行续费  从新响应 jwt
            String newJwt= jwtUtils.createJwt(claims, tokenexpireDate);
            // 需要缓存到Redis中的数据  Jwt续签
            AdminLoginInfoPO adminLoginInfoPO1=AdminLoginInfoPO.builder()
                    .id(id)
                    .enable(enable)
                    .remoteAddr(remoteAddr)
                    .userAgent(userAgent)
                    .authorityListJsonString(authorityListJsonString)
                    .build();
            redisTemplateUtils.set(JwtCacheConsts.ADMIN_JWT_PREFIX+newJwt,adminLoginInfoPO,tokenDuration);

            //AdminLoginInfoVo loginInfoByUsername = adminMapper.getLoginInfoByUsername(username);

            //响应新的对象
            //AdminLoginInfoDTO adminLoginInfoDTO=AdminLoginInfoDTO.builder()
                    //.id(id)
                    //.username(username)
                    //.avatar(loginInfoByUsername.getAvatar())
                    //.token(newJwt)
                    //.tokenExpiration(tokenexpireDate).build();
            //String newAdminLoginInfoDTO = JSON.toJSONString(newJwt);
            //JsonResult<String> stringJsonResult = JsonResult.ok(newAdminLoginInfoDTO);

            //将刷新的 Jwt 转换为Json
            String jsonNewJwt = JSON.toJSONString(newJwt);
            // 设置响应 数据类型
            httpServletResponse.setHeader("Content-Type", "application/json");
            // 向响应头中添加一个名为 "Server" 的自定义字段，并设定值为 "MyCustomServer"
            //httpServletResponse.setHeader("Server", "MyCustomServer");
            // 将刷新的Jwt设置在 响应头中
            httpServletResponse.setHeader("newJwt",jsonNewJwt);
            //jwtUtils.printToken(httpServletRequest,httpServletResponse,stringJsonResult);
            // 在redis中删除原来的快过期key
            redisTemplateUtils.delete(JwtCacheConsts.ADMIN_JWT_PREFIX+jwtToken);
            //admin:jwt:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiZXhwIjoxNjg1ODUyOTA4LCJ1c2VybmFtZSI6InJvb3QxIn0.MG9mx3tlk-QnATURO5ruT-h260Vk1LWR7Fi2r8xC7kw
        }

        //放行
        filterChain.doFilter(httpServletRequest,httpServletResponse);


//        Claims user = jwtUtils.getUser(jwtToken);
//        String userInfo = (String) user.get("user_info");
//        AdminLoginInfoVo adminLoginInfoVo = objectMapper.readValue(userInfo, AdminLoginInfoVo.class);
//        List<String> userAuth = (List<String>) user.get("userAuth");
//        List<SimpleGrantedAuthority> simpleGrantedAuthorities=userAuth.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());
//        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(adminLoginInfoVo,null,simpleGrantedAuthorities);
//        //把token 放到安全上下文，securityContext
//        SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
//        filterChain.doFilter(httpServletRequest,httpServletResponse);//放行

        //admin:jwt:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiZXhwIjoxNjg1ODc1MTI2LCJ1c2VybmFtZSI6InJvb3QxIn0.iiQwxPuYzJ94IYM2_FTI9W_uNvmJYVEdKfvWNP5mxVE
    }
}
